package gazzera.node.core.plugins.shareplugin.db;

import gazzera.node.core.plugins.shareplugin.*;
import gazzera.utils.*;

import java.io.*;

import com.sleepycat.je.*;
import com.sleepycat.persist.*;

public class HashDatabase
{
	private EntityStore store;
	
	private PrimaryIndex<String,FileInfo> files;
    private SecondaryIndex<Hash,String,FileInfo> filesByHash;
	
    private Hasher hasher;
    
    public HashDatabase(Environment env) {
    	StoreConfig storeConfig = new StoreConfig();

        storeConfig.setReadOnly(env.getConfig().getReadOnly());
        storeConfig.setAllowCreate(!env.getConfig().getReadOnly());

        store = new EntityStore(env, "HashDatabase", storeConfig);

    	files = store.getPrimaryIndex(
    			String.class, FileInfo.class);

    	filesByHash = store.getSecondaryIndex(
    			files, Hash.class, "hash");
    	
        //SortedMap<String,FileInfo> k = files.sortedMap(); 
    	
    	
		hasher = new Hasher(this);
		hasher.start();
		
		/*
		try
		{
		    test();
		}
		catch (Exception e)
		{
		    e.printStackTrace();
		}
		*/
    }

    public String getFile(Hash hash)
    {
    	FileInfo info = getFileInfoByHash(hash);
    	
    	if(info == null)
    	{
    		return null;
    	}
    	
    	return info.getFullName();
    }
   
    public void enqueueScan(String filename)
    {
    	hasher.addFileToHash(filename);
    }
    public void cleanEnqueued()
    {
    	hasher.clean();
    }
    public FileInfo getFileInfoByHash(Hash hash)
    {
    	FileInfo info = filesByHash.get(hash);
    	
    	if(info == null)
    	{
    		return null;
    	}
    	
    	File f = new File(info.getFullName());
    	if ( (f.lastModified() != info.timestamp ) || (f.length() != info.size ))
    	{
    		// the file changed, remove it and return null
    		files.delete(info.getFullName());
    		
    		// recurse becouse there could be different file with the same hash, only one of which is valid
    		return getFileInfoByHash(hash);
    	}
    	
    	return info;
    }
   
    public FileID getFileID(File f, ShareList sl, String ourHash)
    {
    	FileID res = getHash(f, true);
    	if(res.equals(Hash.EMPTY))
    	{
    		res = getUserFileId(f, sl, ourHash);
    	}
    	return res;
    }
    
    public UserFileID getUserFileId(File f, ShareList sl, String ourHash)
    {
    	UserFileID id = new UserFileID(ourHash, sl.localToSharedPath(f.getAbsolutePath()), f.lastModified(), f.length());
    	return id;
    }
    
    public Hash getHash(File f, boolean enqueueIfEmpty)
    {
    	String filename = f.getAbsolutePath();
    	FileInfo info = files.get(filename);
    	if(info != null)
    	{
    		if ( (f.lastModified() == info.timestamp ) && (f.length() == info.size ))
    		{
    			return info.hash;
    		}
    		else
        	{
        		// the file changed, remove it and return null
        		files.delete(filename);
        	}
    	}
    	if(enqueueIfEmpty) hasher.addFileToHash(filename);
    	return Hash.EMPTY;
    }
   
    public void close() {
    	hasher.block();
    	try 
    	{
			hasher.join();
		}
    	catch (InterruptedException e) 
    	{
			e.printStackTrace();
		}
    	
        if (store != null) {
            try {
            	flush();
            
                store.close();
                store = null;
            } catch(DatabaseException dbe) {
                System.err.println("Error closing store: " +
                                    dbe.toString());
            }
        }
    }

    
    public void put(Hash hash, File file)
    {
    	FileInfo fi = new FileInfo(file.getAbsolutePath(), file.lastModified(), file.length(), hash);
    	files.put(fi);
    }
    
    public void flush()
    {
    	store.sync();
    }
    
    public void test() throws Exception
    {
    	
    }
    
    
}
